package com.wontube.core.utils.net;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.net.InetAddress;
import java.net.MalformedURLException;
import java.net.ProtocolException;
import java.net.Socket;
import java.net.URL;
import java.net.UnknownHostException;
import java.util.Enumeration;
import java.util.Properties;

import javax.net.SocketFactory;

import com.wontube.core.conf.Proxy;



/**
 * @author yuechao
 * 
 */
public class SocketHttp {
	protected String localIP;
	protected Socket client;
	protected BufferedOutputStream sender;
	protected BufferedInputStream receiver;

	protected URL target;
	private int responseCode = -1;
	private String responseMessage = "";
	private String serverVersion = "";
	private Properties header = new Properties();
	
	// 连接超时设置
	private int timeout = 0;
	
	public SocketHttp(String localIP) {
		this.localIP = localIP;
	}
	
	public int getTimeout() {
		return this.timeout;
	}
	
	public void setTimeout(int value) {
		this.timeout = value;
	}

	/* GET方法根据URL，会请求文件、数据库查询结果、程序运行结果等多种内容 */
	public boolean get(String url) {
		int redirectCount = 0;
		try {
			this.checkHTTP(url); 
			this.openServer(this.target.getHost(), this.target.getPort());
			String cmd = "GET " + getURLFormat(this.target) + " HTTP/1.0\r\n" + getBaseHeads() + "\r\n";
			this.sendMessage(cmd);
			this.receiveMessage();
			if (this.header.containsKey("Location")) {
				if (redirectCount > 10) {
					return false;
				}
				redirectCount++;
				String location = this.header.getProperty("Location");
				return this.get(location);
			} 
			return true;
		} catch (ProtocolException e) {
			e.printStackTrace();
		} catch (UnknownHostException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
		
		return false;
	}

	/*
	 * HEAD方法只请求URL的元信息，不包括URL本身。若怀疑本机和服务器上的 文件相同，用这个方法检查最快捷有效。
	 */
	public boolean head(String url) {
		try {
			this.checkHTTP(url);
			this.openServer(this.target.getHost(), this.target.getPort());
			String cmd = "HEAD " + getURLFormat(this.target) + " HTTP/1.0\r\n" + getBaseHeads() + "\r\n";
			this.sendMessage(cmd);
			this.receiveMessage();
			return true;
		} catch (ProtocolException e) {
			e.printStackTrace();
		} catch (UnknownHostException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
		return false;
	}

	/*
	 * POST方法是向服务器传送数据，以便服务器做出相应的处理。例如网页上常用的 提交表格。
	 */
	public boolean post(String url, String content) {
		int redirectCount = 0;
		try {
			this.checkHTTP(url);
			this.openServer(this.target.getHost(), this.target.getPort());
			StringBuilder sbCmd = new StringBuilder("POST " + getURLFormat(this.target) + "HTTP/1.0\r\n" + getBaseHeads());
			sbCmd.append("Content-type: application/x-www-form-urlencoded\r\n");
			sbCmd.append("Content-length: " + content.length() + "\r\n\r\n");
			sbCmd.append(content + "\r\n");
			this.sendMessage(sbCmd.toString());

			if (this.header.containsKey("Location")) {
				if (redirectCount > 10) {
					return false;
				}
				redirectCount++;
				String location = this.header.getProperty("Location");
				return this.post(location, content);
			}
			return true;
		} catch (ProtocolException e) {
			e.printStackTrace();
		} catch (UnknownHostException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
		return false;
	}
	
	public BufferedInputStream getInputStream() {
		return this.receiver;
	}

	protected void checkHTTP(String url) throws ProtocolException {
		try {
			URL target = new URL(url);
			if (target == null || !target.getProtocol().toUpperCase().equals("HTTP")) {
				throw new ProtocolException("Not http protocol.");
			}
			this.target = target;
		} catch (MalformedURLException e) {
			throw new ProtocolException("Protocol format error.");
		}
	}

	/*
	 * 与Web服务器连接。若找不到Web服务器，InetAddress会引发UnknownHostException
	 * 异常。若Socket连接失败，会引发IOException异常。
	 */
	protected void openServer(String host, int port) throws IOException  {
		this.header.clear();
		this.responseMessage = "";
		this.responseCode = -1;

		this.close();

		SocketFactory factory = SocketFactory.getDefault();
		Proxy proxyConf = Proxy.defaultProxy;
		if (proxyConf.isUseProxy()) {
			this.client = factory.createSocket(proxyConf.getProxyAddress(), proxyConf.getProxyPort());
		} else {
			InetAddress localAddress = "".equals(this.localIP) ? InetAddress.getLocalHost() : InetAddress.getByName(this.localIP);
			this.client = factory.createSocket(host, port < 0 ? 80 : port, localAddress, 0);
		}

		this.client.setSoTimeout(this.timeout);
		this.client.setSoLinger(true, 0);
		this.client.setReuseAddress(true);
		this.sender = new BufferedOutputStream(client.getOutputStream());
		this.receiver = new BufferedInputStream(client.getInputStream());
	}

	public void close() {
		if (this.client == null) {
			return;
		}
		try {
			if (this.sender != null) {
				this.sender.close();
			}
			if (this.receiver != null) {
				this.receiver.close();
			}
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			try {
				this.client.close();
			} catch (IOException e) {
			}
			this.client = null;
			this.sender = null;
			this.receiver = null;
		}
	}

	protected String getURLFormat(URL target) {
		String spec = "http://" + target.getHost();
		if (target.getPort() != -1) {
			spec += ":" + target.getPort();
		}
		return spec += target.getFile();
	}

	/* 向Web服务器传送数据 */
	protected void sendMessage(String data) throws IOException {
		this.sender.write(data.getBytes(), 0, data.length());
		this.sender.flush();
	}

	/* 接收来自Web服务器的数据 */
	protected void receiveMessage() throws IOException {
		byte data[] = new byte[1024];
		int count = 0;
		int word = -1;
		// 解析第一行
		while ((word = this.receiver.read()) != -1) {
			if (word == '\r' || word == '\n') {
				word = this.receiver.read();
				if (word == '\n') {
					word = this.receiver.read();
				}
				break;
			}
			if (count == data.length) {
				data = addCapacity(data);
			}
			data[count++] = (byte) word;
		}
		String message = new String(data, 0, count);
		int mark = message.indexOf(32);
		this.serverVersion = message.substring(0, mark);
		while (mark < message.length() && message.charAt(mark + 1) == 32) {
			mark++;
		}
		this.responseCode = Integer.parseInt(message.substring(mark + 1, mark += 4));
		this.responseMessage = message.substring(mark, message.length()).trim();

		// 应答状态码和处理请读者添加
		switch (this.responseCode) {
		case 400:
			throw new IOException("Error request");
		case 404:
			throw new FileNotFoundException(getURLFormat(this.target));
		case 503:
			throw new IOException("Server not available.");
		}
		if (word == -1) {
			throw new ProtocolException("Recive message exception.");
		}
		int symbol = -1;
		count = 0;
		// 解析元信息
		while (word != '\r' && word != '\n' && word > -1) {
			if (word == '\t') {
				word = 32;
			}
			if (count == data.length) {
				data = addCapacity(data);
			}
			data[count++] = (byte) word;
			parseLine: {
				while ((symbol = this.receiver.read()) > -1) {
					switch (symbol) {
					case '\t':
						symbol = 32;
						break;
					case '\r':
					case '\n':
						word = this.receiver.read();
						if (symbol == '\r' && word == '\n') {
							word = this.receiver.read();
							if (word == '\r') {
								word = this.receiver.read();
							}
						}
						if (word == '\r' || word == '\n' || word > 32) {
							break parseLine;
						}
						symbol = 32;
						break;
					}
					if (count == data.length) {
						data = addCapacity(data);
					}
					data[count++] = (byte) symbol;
				}
				word = -1;
			}
			message = new String(data, 0, count);
			mark = message.indexOf(':');
			String key = null;
			if (mark > 0) {
				key = message.substring(0, mark);
			}
			mark++;
			while (mark < message.length() && message.charAt(mark) <= 32) {
				mark++;
			}
			String value = message.substring(mark, message.length());
			this.header.put(key, value);
			count = 0;
		}

		// 可以使用reciver流获得正文数据了		
	}
	
	public String getResponseMessage() {
		return this.responseMessage;
	}

	public int getResponseCode() {
		return this.responseCode;
	}

	public String getServerVersion() {
		return this.serverVersion;
	}

	public synchronized String getHeaderKey(int i) {
		if (i >= this.header.size()) {
			return null;
		}

		Enumeration<?> e = this.header.propertyNames();
		String key = null;
		for (int j = 0; j <= i; j++) {
			key = (String) e.nextElement();
		}
		
		return key;
	}

	public synchronized String getHeaderValue(int i) {
		if (i >= this.header.size()) {
			return null;
		}
		
		return this.header.getProperty(getHeaderKey(i));
	}

	public synchronized String getHeaderValue(String key) {
		return this.header.getProperty(key);
	}

	protected String getBaseHeads() {
		String inf = "User-Agent: Mozilla/5.0 (Windows NT 5.1) AppleWebKit/535.7 (KHTML, like Gecko) Chrome/16.0.912.63 Safari/535.7\r\n" 
				+ "Accept:text/html,application/xhtml+xml,application/xml;*/*\r\n";
		
		return inf;
	}

	private byte[] addCapacity(byte rece[]) {
		byte temp[] = new byte[rece.length + 1024];
		System.arraycopy(rece, 0, temp, 0, rece.length);
		
		return temp;
	}
}
